package com.zzyk.main.configuration.shiro.realm;


import cn.hutool.core.date.DateTime;
import com.zzyk.main.configuration.shiro.matcher.UserCredentialsMatcher;

import com.zzyk.main.model.pojo.SysPermission;
import com.zzyk.main.model.pojo.SysRole;
import com.zzyk.main.model.pojo.SysUser;
import com.zzyk.main.service.ISysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Shiro权限管理的认证细节在这里实现
 */
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private ApplicationContext ctx;
    private ISysUserService userService;
    /**
     * 限定这个 Realm 只处理 UsernamePasswordToken
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }
    // 这块是权限鉴定 就是登录成功后把权限查出来
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 一定要这么写 绝对不能用自动注入 否则多数据源将失效(注入顺序的原因)
        if (this.userService == null) {
            this.userService = ctx.getBean(ISysUserService.class);
        }
        // 首先要把用户查出来
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        SysUser user = userService.getUserWithRoleAndPermission(sysUser.getUsername());
        // 然后把他的角色和权限查出来 扔进
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> roleSet = new HashSet<>();
        Set<String> permissionSet = new HashSet<>();
        for (SysRole role : user.getRoleSet()) {
            roleSet.add(role.getRoleName());
            for (SysPermission permission : role.getPermissionSet()) {
                permissionSet.add(permission.getPermissionName());
            }
        }
        info.setRoles(roleSet);
        info.setStringPermissions(permissionSet);
        return info;
    }

    // 这块是身份鉴定 相当于登录
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (this.userService == null) {
            this.userService = ctx.getBean(ISysUserService.class);
        }
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        SysUser user = userService.login(token.getUsername());
        // 我这块就写了一个异常
        if (user == null) {
            throw new UnknownAccountException();
        } else if (user.getStatus() == 0) {
            throw new DisabledAccountException("账号审核中，审核通过会有短信告知，急用联系渠道人员。");
        } else {
            Date expire = user.getExpire();
            if (expire != null) {
                DateTime ex = new DateTime(expire);
                if (ex.isBefore(new Date())) {
                    SysUser userInfo =new SysUser();
                    userInfo.setId(user.getId());
                    userInfo.setUpdateTime(new Date());
                    userInfo.setStatus(0);
                    userService.updateUser(userInfo);
                    throw new DisabledAccountException("账号已过期");
                }
            }
        }

        // 实际上有好些异常可以往外抛
        // IncorrectCredentialsException 凭证错误 可以理解为密码错误
        // DisabledAccountException 账号被禁用
        // LockedAccountException 账号被锁定
        // ExcessiveAttemptsException 登录失败次数超过限制
        // ... 还有好些 他们都是 AuthenticationException的子类

        // 这里返回一个认证信息
        return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }

    // 因为使用了自定义验证密码的方法 所以在这里要用自定义验证密码的方法替换默认的方法
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        super.setCredentialsMatcher(new UserCredentialsMatcher());
    }
}
